#include "pch.h"
#include "api.h"
#include <mongoose.h>
#include <nlohmann/json.hpp>
#include "common.h"
#include "msghook.h"
#include <exception>  

#pragma comment(lib, "ws2_32.lib")
using namespace std;
using namespace nlohmann;

#define STR2INT(str) (is_digit(str) ? stoi(str) : 0)
#define WS2LW(wstr) (LPWSTR) wstr.c_str()

static bool kHttpRuning = false;
static HANDLE kHttpThread = NULL;

bool is_digit(string str) {
  if (str.length() == 0) {
    return false;
  }

  for (auto it : str) {
    if (it < '0' || it > '9') {
      return false;
    }
  }
  return true;
}

string get_var(mg_http_message *hm, string name) {
  string ret;
  char *buffer = new char[hm->query.len + 1];
  ZeroMemory(buffer, hm->query.len + 1);
  int len = mg_http_get_var(&hm->query, name.c_str(), buffer, hm->query.len);
  if (len > 0) ret = string(buffer, len);
  delete[] buffer;
  buffer = NULL;
  return ret;
}
/// @brief 获取request中的请求参数int类型
/// @param hm 消息
/// @param data json数据
/// @param key  key
/// @param method 是否是post，暂时全部用post
/// @return int
static int  get_http_req_param_int(mg_http_message *hm, json data, string key, int method){
    int result;
    switch (method) {
      case 0: {
        result = STR2INT(get_var(hm,key).c_str());
        break;
      }
      case 1: {
        try {
          result = data[key].get<int>();
        } catch (json::exception) {
          result = STR2INT(data[key].get<string>());
        }
        break;
      }
      default:
        break;
    }
    return result;
}

/// @brief 获取request中的请求参数
/// @param hm 消息
/// @param data json数据
/// @param key  key
/// @param method 是否是post，暂时全部用post
/// @return 
static wstring  get_http_req_param(mg_http_message *hm, json data, string key, int is_post){
    wstring result;
    switch (is_post) {
      case 0: {
        result = utf8_to_unicode(get_var(hm,key).c_str());
        break;
      }
      case 1: {
		auto k = data[key];
        result = utf8_to_unicode(data[key].get<string>().c_str());
        break;
      }
      default:
        break;
    }
    return result;
}

static unsigned long long get_http_param_ulong64(mg_http_message *hm,
                                                 json j_data, string key,
                                                 int method) {
    unsigned long long result = 0;
    switch (method) {
      case 0: {
        string value = get_var(hm, key);
        istringstream is(value);
        is >> result;
        break;
      }
      case 1: {
        try {
          result = j_data[key].get<ULONG64>();
        } catch (json::exception) {
          string value = j_data[key].get<string>();
          istringstream is(value);
          is >> result;
        }
        break;
      }
      default:
        break;
    }
    return result;
}

static int get_http_param_int(mg_http_message *hm, json j_data, string key,
                              int method) {
  int result = 0;
  switch (method) {
    case 0: {
      result = STR2INT(get_var(hm, key));
      break;
    }
    case 1: {
      try {
        result = j_data[key].get<int>();
      } catch (json::exception) {
        result = STR2INT(j_data[key].get<string>());
      }
      break;
    }
    default:
      break;
  }
  return result;
}

static vector<wstring> get_http_param_array(mg_http_message *hm, json j_data,
                                            string key, int method) {
  vector<wstring> result;
  switch (method) {
    case 0: {
      result = split(utf8_to_unicode(get_var(hm, key).c_str()), L',');
      break;
    }
    case 1: {
      result = split(utf8_to_unicode(j_data[key].get<string>().c_str()), L',');
      break;
    }
    default:
      break;
  }
  return result;
}

/// @brief api接口入口解析
/// @param hm mg_http_message
/// @param c connection
/// @param ret json数据
void api_handle(mg_http_message *hm, struct mg_connection *c, string &ret) {
  int is_post = 0;

  if (mg_vcasecmp(&hm->method, "POST") == 0) {
    is_post = 1;
  }
  #ifdef _DEBUG
  printf("method:%s body: %s", hm->method.ptr,hm->body.ptr);
  #endif
  if (is_post == 0){
    json ret_data = {{"result", "ERROR"}, {"msg", "not support method"}};
    ret = ret_data.dump();
    return;
  }
  
  json j_param =
      json::parse(hm->body.ptr, hm->body.ptr + hm->body.len, nullptr, false);
  if (hm->body.len != 0 && j_param.is_discarded() == true) {
    json ret_data = {{"result", "ERROR"}, {"msg", "json string is invalid."}};
    ret = ret_data.dump();
    return;
  }
  int api_number = STR2INT(get_var(hm, "type"));
  switch (api_number) {
    case WECHAT_IS_LOGIN: {
      int success = CheckLogin();
      json ret_data = {{"result", "OK"}, {"code", success}};
      ret = ret_data.dump();
      break;
    }
    case WECHAT_MSG_START_HOOK: {
      wstring ip = get_http_req_param(hm, j_param, "ip", is_post);
      string client_ip = Wstring2String(ip);
      int port = get_http_req_param_int(hm, j_param, "port", is_post);
      char ip_cstr[16];
      strcpy_s(ip_cstr,client_ip.c_str());
      int success = HookRecvMsg(ip_cstr,port);
      json ret_data = {{"code", success}, {"result", "OK"}};
      ret = ret_data.dump();
      break;
    }
    
    default:
    break;
  }
}

/// @brief 事件回调函数
/// @param c 链接
/// @param ev 事件
/// @param ev_data 事件数据
/// @param fn_data 回调数据
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
  struct mg_http_serve_opts opts = {0};
  if (ev == MG_EV_HTTP_MSG) {
    struct mg_http_message *hm = (struct mg_http_message *)ev_data;
    string ret = R"({"result":"OK"})";
    if (mg_http_match_uri(hm, "/api/")) {
      try {
        api_handle(hm, c, ret);
      } catch (exception &e) {
        json res = {{"result", "ERROR"}, {"msg", e.what()}};
        ret = res.dump();
      }
      if (ret != "") {
        mg_http_reply(c, 200, "", ret.c_str(), 0, 0);
      }
    } else {
      mg_http_reply(c, 500, NULL, "%s", "Invalid URI");
    }
  }
  (void)fn_data;
}
/// @brief http server
/// @param port  端口
void http_server(int port) {
  string lsten_addr = "http://0.0.0.0:" + to_string(port);
  struct mg_mgr mgr;
  // Init manager
  mg_mgr_init(&mgr);
  // Setup listener
  mg_http_listen(&mgr, lsten_addr.c_str(), fn, &mgr);
  // Event loop
  for (;;) mg_mgr_poll(&mgr, 1000);
  // Cleanup
  mg_mgr_free(&mgr);
}

/// @brief 启动http服务
/// @param port 端口
/// @return 成功返回0
int http_start(int port) {
  if (kHttpRuning) {
    return 1;
  }
  #ifdef _DEBUG
    CreateConsole();
  #endif
  kHttpRuning = true;
  kHttpThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)http_server,
                              (LPVOID)port, NULL, 0);
  return 0;
}

int http_close() {
  if (!kHttpRuning) {
    return 1;
  }
  #ifdef _DEBUG
    CloseConsole();
  #endif
  if (kHttpThread) {
    WaitForSingleObject(kHttpThread, -1);
    CloseHandle(kHttpThread);
    kHttpThread = NULL;
  }
  kHttpRuning = false;
  return 0;
}